#include <fs.h>
#include "device.h"
size_t ramdisk_read(void *buf, size_t offset, size_t len);
size_t ramdisk_write(const void *buf, size_t offset, size_t len);
typedef size_t (*ReadFn) (void *buf, size_t offset, size_t len);
typedef size_t (*WriteFn) (const void *buf, size_t offset, size_t len);

typedef struct {
  char *name;
  size_t size;
  size_t disk_offset;
  ReadFn read;
  WriteFn write;
	size_t open_offset;
} Finfo;

enum {FD_STDIN, FD_STDOUT, FD_STDERR,FD_EVENT,FD_FB,FD_DISPINFO};

size_t invalid_read(void *buf, size_t offset, size_t len) {
  panic("should not reach here");
  return 0;
}

size_t invalid_write(const void *buf, size_t offset, size_t len) {
  panic("should not reach here");
  return 0;
}
/* This is the information about all files in disk. */
static Finfo file_table[] __attribute__((used)) = {
  [FD_STDIN]  = {"stdin", 0, 0, invalid_read, invalid_write},
  [FD_STDOUT] = {"stdout", 0, 0, invalid_read, serial_write},
  [FD_STDERR] = {"stderr", 0, 0, invalid_read, serial_write},
  [FD_EVENT] 	= {"/dev/event", 0, 0,events_read, invalid_write},
  [FD_FB] 		= {"/dev/fb", 0, 0,invalid_read, fb_write},
	[FD_DISPINFO] = {"/proc/dispinfo",0,0,dispinfo_read,invalid_write},
#include "files.h"
};

char* get_file_name(int fd){return file_table[fd].name;}

void init_fs() {
  // TODO: initialize the size of /dev/fb
	file_table[FD_FB].size =io_read(AM_GPU_CONFIG).width*io_read(AM_GPU_CONFIG).height*sizeof(uint32_t);
}

int fs_open(const char *pathname, int flags, int mode){
	for(int i=0;file_table[i].name != NULL; i++){
		if(strcmp(pathname,file_table[i].name) == 0){
			return i;
		}
	}	
	panic("fopen could not find this file");
}
size_t fs_read(int fd, void *buf, size_t len){
	if(file_table[fd].read == NULL){
	size_t len1;
	if(file_table[fd].open_offset + len < file_table[fd].size) len1 = len;
	else len1 = file_table[fd].size - file_table[fd].open_offset;
	ramdisk_read(buf,file_table[fd].disk_offset + file_table[fd].open_offset,len1);
	file_table[fd].open_offset += len1;
	return len1;
	}else{
		return file_table[fd].read(buf,0,len);
	}
}	
size_t fs_write(int fd, const void *buf, size_t len){
	//write real fs
	if(file_table[fd].write == NULL){
	size_t len1;
		if(file_table[fd].open_offset + len < file_table[fd].size) len1 = len;
		else len1 = file_table[fd].size - file_table[fd].open_offset;
		ramdisk_write(buf,file_table[fd].disk_offset + file_table[fd].open_offset,len1);
		file_table[fd].open_offset += len1;
		return len1;
	}
	//write the special file
	else{
		size_t offset = file_table[fd].disk_offset + file_table[fd].open_offset;
		//printf("fd = %d,len = %d,disk_offset = %d,open_offset = %d\n",fd,len,file_table[fd].disk_offset,file_table[fd].open_offset);
		return file_table[fd].write(buf,offset,len);	
	}
}	
int fs_close(int fd){
	file_table[fd].open_offset = 0;
	return 0;
}
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
size_t fs_lseek(int fd, size_t offset, int whence){
	size_t open_offset_new = 0;
	if(whence == SEEK_SET)open_offset_new = offset;
	if(whence == SEEK_CUR)open_offset_new = file_table[fd].open_offset + offset;
	if(whence == SEEK_END)open_offset_new = file_table[fd].size + offset;
	
	assert( open_offset_new<= file_table[fd].size);
	assert(open_offset_new >= 0);
	file_table[fd].open_offset = open_offset_new;
	return open_offset_new;
}

